﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using AnguaRPGEngine.Library;
using AnguaRPGEngine.Library.Forms;
using AnguaRPGEngine.Library.Controls;
using AnguaRPGEngine.Library.Dice;
using AnguaRPGEngine.Library.Systems;
using AnguaRPGEngine.Library.Configuration;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using AnguaRPGEngine.Library.Interfaces;
using AnguaRPGEngine.Library.Sockets;
using AnguaRPGEngine.Library.Packages;
using AnguaRPGEngine.Library.Events;
using AnguaRPGEngine.Library.Events.Messages;

namespace AnguaRPGEngine.PCClient
{
    public partial class PCClientMainForm : Form
    {
        [ImportMany]
        public Lazy<IARPGEModule, IDictionary<string, object>>[] _modules { get; set; }
        private IARPGEModule _module;
        private PackageBuffer _Buffer;
        private Point _DragStart;
        private CompositionContainer _Container;
        private DiceTower _diceTower;
        private Form _PlayerForPC;
        private ClientSocket _ClientSocket;
        private System.Timers.Timer _BufferTimer;
        private Process _MapProcess;
        private ClientSocket _MapSocket;
        public PCClientMainForm()
        {
            InitializeComponent();
            GameSystem.Instance.Initialize(false);
            ImageLibrary.Instance.LoadImageLibrary(GameSystem.Instance.PathToData);
            DieRoller.Instance.LoadDiceCollection();
            _Buffer = new PackageBuffer();
            _BufferTimer = new System.Timers.Timer(1000);
            _BufferTimer.AutoReset = false;
            _BufferTimer.Elapsed += new System.Timers.ElapsedEventHandler(_BufferTimer_Elapsed);
            _BufferTimer.Start();
        }

        void _BufferTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            ProcessBuffer();
            _BufferTimer.Start();

        }
        private bool ProcessBuffer()
        {
            BufferPackage package = _Buffer.GetMessage();
            if (package != null)
            {


                int cmd = int.Parse(package.Package.Substring(0, package.Package.IndexOf("=")));
                string pkg = package.Package.Remove(0, package.Package.IndexOf("=") + 1);
                switch (cmd)
                {
                    case Constants.WINDOW_LOCATION:
                        WindowPosition wp = Serializer<WindowPosition>.DeserializeFromString(pkg);
                        FormPosition fp = null;
                        try
                        {

                            fp = GameSystem.Instance.ClientConfig.FormPositions.Find(p => p.FormName == "Map");
                        }
                        catch
                        {
                            fp = null;
                        }
                        if (fp != null)
                        {
                            fp.Location = wp.Location;
                            fp.Size = wp.Size;
                            fp.Visible = wp.Visible;
                        }
                        GameSystem.Instance.SaveConfiguration();
                        break;
                    case Constants.BROADCAST_PC:
                        // this is an update or add from the GM, so push it to the map via the _module
                           string acPkg = _module.AddCharacter(pkg);
                        _MapSocket.Send(Tools.CreatePackage(Constants.PC_UPDATE, acPkg));
                        break;
                    case Constants.LOGON_RESULTS:
                        break;
                }
                _module.ProcessPackage(package);
                return true;
            }
            return false;
        }
        private CompositionContainer GetContainerFromDirectory()
        {
            var catalog = new AggregateCatalog();
            var thisAssembly =
            new AssemblyCatalog(
            System.Reflection.Assembly.GetExecutingAssembly());

            catalog.Catalogs.Add(thisAssembly);

            catalog.Catalogs.Add(
            new DirectoryCatalog(GameSystem.Instance.PathToDLLs));

            var container = new CompositionContainer(catalog);
            return container;
        }
        private bool Compose()
        {
            _Container = GetContainerFromDirectory();

            try
            {
                _Container.ComposeParts(this);
            }
            catch
            (CompositionException compException)
            {
                MessageBox.Show(compException.ToString());
                return false;
            }

            return true;
        }
        private void PCClientMainForm_Load(object sender, EventArgs e)
        {
            using (SplashScreen ss = new SplashScreen(string.Format("PC Client v{0}", Application.ProductVersion)))
            {
                ss.ShowDialog();
            }
            #region Form Positioning Application
            FormPosition fp = null;
            try
            {
                fp = GameSystem.Instance.ClientConfig.FormPositions.Find(p => p.FormName == this.Name);
            }
            catch
            {
                fp = null;
            }
            if (fp == null)
            {
                this.Left = 0;
                this.Top = 0;
                this.Width = Screen.PrimaryScreen.Bounds.Width;
                fp = new FormPosition();
                fp.FormName = this.Name;
                fp.Location = this.Location;
                fp.Size = this.Size;
                fp.Visible = this.Visible;
                GameSystem.Instance.ClientConfig.FormPositions.Add(fp);
            }
            else
            {
                this.Location = fp.Location;
                this.Size = fp.Size;
                this.Visible = fp.Visible;
            }
            #endregion

            #region Show The Dice Tower
            _diceTower = new DiceTower(this);
            _diceTower.Show();
            #endregion

            #region MEF Composition
            bool successfulCompose = Compose();
            if (successfulCompose)
            {
                foreach (var export in _modules)
                {
                    var ID = export.Metadata["ID"] as string;
                    IARPGEModule module = export.Value as IARPGEModule;
                    ToolStripMenuItem tsmi = new ToolStripMenuItem(module.Name);
                    tsmi.Click += new EventHandler(tsmi_Click);
                    modulesToolStripMenuItem.DropDownItems.Insert(0, tsmi);
                    if (ID.ToUpper() == Properties.Settings.Default.ModuleID.ToUpper())
                    {
                        _module = module;
                        tsmi.Checked = true;
                    }

                }
            }
            #endregion

            lblGameName.Text = _module.GameName;
            lblModuleName.Text = _module.Name;

            SetRulesMenu();
            #region _PlayersForGM Form
            _PlayerForPC = _module.GetPlayerForPC(this);
            _PlayerForPC.Left = 0;
            _PlayerForPC.Top = 100;
            _PlayerForPC.Show();
            #endregion

            #region Map
            _MapProcess = new Process();
#if DEBUG
            _MapProcess.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory().Replace("PCClient", "Map");
#endif
            _MapProcess.StartInfo.Arguments = string.Format("/M:{0} /P:{1}", Constants.PC_CLIENT, Properties.Settings.Default.MapPort);
            _MapProcess.StartInfo.FileName = "AnguaRPGEngine.Map.exe";
            _MapProcess.Start();
            System.Threading.Thread.Sleep(1000);
            _MapSocket = new ClientSocket();
            _MapSocket.OnReceive += new ReceiveEventHandler(_MapSocket_OnReceive);
            _MapSocket.OnSocketError += new SocketErrorEventHandler(_MapSocket_OnSocketError);
            _MapSocket.OnConnect += new ConnectEventHandler(_MapSocket_OnConnect);
            _MapSocket.StartClient("localhost", Properties.Settings.Default.MapPort);
            _MapSocket.StartConnect();
            fp = null;
            try
            {
                fp = GameSystem.Instance.ClientConfig.FormPositions.Find(p => p.FormName == "Map");
            }
            catch
            {
                fp = null;
            }
            if (fp == null)
            {
                WindowPosition wp = new WindowPosition();
                wp.Location = new Point(this.Left, this.Top + this.Height);
                wp.Size = new Size(300, 300);
                wp.WindowName = "Map";
                wp.Visible = true;
                fp = new FormPosition();
                fp.FormName = "Map";
                fp.Location = wp.Location;
                fp.Size = wp.Size;
                fp.Visible = wp.Visible;
                GameSystem.Instance.ClientConfig.FormPositions.Add(fp);
                string wpSerialized = "";
                Serializer<WindowPosition>.SerializeToString(wp, out wpSerialized);
                _MapSocket.Send(Tools.CreatePackage(Constants.WINDOW_LOCATION, wpSerialized));
            }
            else
            {
                WindowPosition wp = new WindowPosition();
                wp.Location = fp.Location;
                wp.Size = fp.Size;
                wp.WindowName = "Map";
                wp.Visible = true;// fp.Visible;
                string wpSerialized = "";
                Serializer<WindowPosition>.SerializeToString(wp, out wpSerialized);
                _MapSocket.Send(Tools.CreatePackage(Constants.WINDOW_LOCATION, wpSerialized));
            }

            #endregion

        }

        void _MapSocket_OnConnect()
        {
            _MapSocket.StartReceive();
        }

        void _MapSocket_OnSocketError(object sender, System.Net.Sockets.SocketException e)
        {

            MessageBox.Show(e.Message);
        }



        void _MapSocket_OnReceive(byte[] data, long numBytes)
        {
            // add to buffer
            _Buffer.AddMessage(new Guid(), Tools.GetStringFromPackage(data, (int)numBytes));

        }

        void SetRulesMenu()
        {
            _module.SetRulesMenu(ref rulesToolStripMenuItem);
            ToolStripMenuItem tsmiEditBooks = new ToolStripMenuItem("Edit Books");
            tsmiEditBooks.Click += new EventHandler(tsmiEditBooks_Click);
            rulesToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator());
            rulesToolStripMenuItem.DropDownItems.Add(tsmiEditBooks);
        }
        void tsmiEditBooks_Click(object sender, EventArgs e)
        {
            if (_module.EditBooks() == DialogResult.OK)
            {
                SetRulesMenu();
            }
        }
        void tsmi_Click(object sender, EventArgs e)
        {
            // load this mod
        }

        private void diceTowerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _diceTower.Visible = true;
            _diceTower.BringToFront();
        }

        private void PCClientMainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            GameSystem.Instance.IsShuttingDown = true;
            _diceTower.Close();
            _PlayerForPC.Close();
            _MapProcess.CloseMainWindow();
            _MapProcess.WaitForExit();
            _BufferTimer.Stop();
            while (ProcessBuffer()) ;
            FormPosition fp = null;
            try
            {
                fp = GameSystem.Instance.ClientConfig.FormPositions.Find(p => p.FormName == this.Name);
            }
            catch
            {
                fp = null;
            }
            if (fp == null)
            {

                fp = new FormPosition();
                fp.FormName = this.Name;
                fp.Location = this.Location;
                fp.Size = this.Size;
                fp.Visible = this.Visible;
                GameSystem.Instance.ClientConfig.FormPositions.Add(fp);
            }
            else
            {
                fp.Location = this.Location;
                fp.Size = this.Size;
                fp.Visible = this.Visible;
            }
            GameSystem.Instance.Shutdown();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void playersToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _PlayerForPC.Visible = true;
            _PlayerForPC.BringToFront();
        }

        private void PCClientMainForm_LocationChanged(object sender, EventArgs e)
        {
            ParentFormMoveEvent pfme = new ParentFormMoveEvent(this.Location, this.Size);
            ApplicationController.EventAggregator.SendMessage(pfme);
        }

        private void PCClientMainForm_ResizeEnd(object sender, EventArgs e)
        {
            ParentFormMoveEvent pfme = new ParentFormMoveEvent(this.Location, this.Size);
            ApplicationController.EventAggregator.SendMessage(pfme);
        }

        private void aboutAnguaRPGEngineToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void defaultPositionsAndSizesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _diceTower.Left = this.Left + this.Width - _diceTower.Width;
            _diceTower.Top = this.Top + this.Height + 10;
        }

        private void joinToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _module.NewCharacter(this);
        }

        private void loadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _module.LoadCharacter();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _module.SaveCharacter();
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _module.SaveCharacter(true);
        }


    }
}
